package org.seckill.service.impl;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.MapUtils;
import org.seckill.dao.SeckillDao;
import org.seckill.dao.SuccessKilledDao;
import org.seckill.dao.cache.RedisDao;
import org.seckill.dto.Exposer;
import org.seckill.dto.SeckillExcution;
import org.seckill.entity.Seckill;
import org.seckill.entity.SuccessKilled;
import org.seckill.enums.SeckillStatEnum;
import org.seckill.exception.RepeatKillException;
import org.seckill.exception.SeckillCloseException;
import org.seckill.exception.SeckillException;
import org.seckill.service.SeckillService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
@Service
public class SeckillServiceImpl implements SeckillService{
	private Logger logger = LoggerFactory.getLogger(this.getClass());
	@Autowired
	private SeckillDao seckillDao;
	@Autowired
	private SuccessKilledDao successKilledDao;
	@Autowired
	private RedisDao redisDao;
	//md5的盐值
	private final String salt = "dfadffsdf888dfd8";
	public List<Seckill> getSeckillList() {
		return seckillDao.queryAll(0, 4);
	}

	public Seckill getById(long seckillId) {
		return seckillDao.queryById(seckillId);
	}
	/**
	 * 暴露秒杀地址
	 */
	public Exposer exportSeckillUrl(long seckillId) {
		//从redisDao中去取数据，如果没有就去mysql中去取，存入redis
		Seckill seckill=redisDao.getSeckill(seckillId);
		if(seckill==null){
			seckill = seckillDao.queryById(seckillId);
			if(seckill == null){
				return new Exposer(false, seckillId);
			}else{
				redisDao.setSeckill(seckill);
			}
		}
		System.out.println(seckill);
		Date startTime = seckill.getStartTime();
		Date endTime = seckill.getEndTime();
		Date nowTime = new Date();
		if(nowTime.getTime()<startTime.getTime()  || nowTime.getTime() >endTime.getTime() ){
			System.out.println(nowTime);
			System.out.println(startTime);
			System.out.println(endTime);
			return new Exposer(false, seckillId,nowTime.getTime(), startTime.getTime(), endTime.getTime());
		}
		String md5=this.getMd5(seckillId);
		return new Exposer(true, md5, seckillId);
	}
	/**
	 * 执行秒杀操作
	 */
	public SeckillExcution executeSeckill(long seckillId, long userPhone,
			String md5) throws SeckillException, RepeatKillException,
			SeckillCloseException {
		try{
		if(md5 == null || !md5.equals(this.getMd5(seckillId))){
			throw new SeckillException("seckill data rewrite");
		}
		
			Date nowTime = new Date();
//			//执行秒杀逻辑：减库存+购买记录行为
//			int updateCount = seckillDao.reduceNumber(seckillId, nowTime);
//			if(updateCount <=0){
//				throw new SeckillCloseException("seckill is closed");
//			}else{
//				int insertCount=successKilledDao.insertSuccessKilled(seckillId, userPhone);
//				//联合主键：seckillId,userPhone
//				if(insertCount<=0){
//					throw new RepeatKillException("seckill repeated");
//				}else{
//					//秒杀成功
//					SuccessKilled successKilled = successKilledDao.queryByIdWithSeckill(seckillId, userPhone);
//					return new SeckillExcution(seckillId, SeckillStatEnum.SUCCESS, successKilled);
//				}
//			
//			}
			//优化一下：先插入数据，再减库存,减少行锁的持有时间
			int insertCount=successKilledDao.insertSuccessKilled(seckillId, userPhone);
			if(insertCount<=0){
				throw new RepeatKillException("seckill repeated");
			}else{
				int updateCount = seckillDao.reduceNumber(seckillId, nowTime);
				if( updateCount <=0){
					throw new SeckillCloseException("seckill is closed");
				}else{
					//秒杀成功
					SuccessKilled successKilled = successKilledDao.queryByIdWithSeckill(seckillId, userPhone);
					return new SeckillExcution(seckillId, SeckillStatEnum.SUCCESS, successKilled);
				}
			}
		}catch(SeckillCloseException e1){
			throw e1;
		}catch(RepeatKillException e2){
			throw e2;
		}catch(Exception e){
			logger.error(e.getMessage(),e);
			//所有编译期异常，转化为运行期异常
			throw new SeckillException("seckill inner error"+e.getMessage());
		}
	
	}
	/**
	 * 通过id+salt进行腌制计算
	 * @param seckillId
	 * @return
	 */
	private String getMd5(long seckillId){
		String base = seckillId +"/"+salt;
		String md5 = DigestUtils.md5DigestAsHex(base.getBytes());
		return md5;
	}
	/**
	 * 执行秒杀通过存储过程
	 */
	@Transactional
	public SeckillExcution executeSeckillByProc(long seckillId, long userPhone,
			String md5)
			throws SeckillException, RepeatKillException, SeckillCloseException {
		try{
			if(md5 == null || !md5.equals(this.getMd5(seckillId))){
				throw new SeckillException("seckill data rewrite");
			}
			Map<String,Object> map = new HashMap<String,Object>();
			map.put("seckillId", seckillId);
			map.put("userPhone", userPhone);
			map.put("nowTime", new Date());
			map.put("result", null);
			successKilledDao.execute_seckill(map);
			int result=MapUtils.getInteger(map, "result");
			if(result==-1){
				throw new RepeatKillException("seckill repeated");
			}else if(result==-2){
				throw new SeckillCloseException("seckill is closed");
			}else if(result==0){
					//秒杀成功
					SuccessKilled successKilled = successKilledDao.queryByIdWithSeckill(seckillId, userPhone);
					return new SeckillExcution(seckillId, SeckillStatEnum.SUCCESS, successKilled);
		    }else{
		    	throw new SeckillException("inner error");
		    }
		}catch(SeckillCloseException e1){
			throw e1;
		}catch(RepeatKillException e2){
			throw e2;
		}catch(SeckillException e3){
			throw e3;
		}catch(Exception e){
			logger.error(e.getMessage(),e);
			//所有编译期异常，转化为运行期异常
			throw new SeckillException("seckill inner error"+e.getMessage());
		}
	}


}
